/*******************************************************************
 *
 * Copyright (C), 2021-2022, Letstrong, All rights reserved.
 *
 * Author      : 
 * Create Time : 2020-12-14
 * Version     : v0.1
 * Description : 
 *******************************************************************/

/*----------------------------Head file----------------------------*/
#include "rwip_config.h"

#if (BLE_OTA_SERVER)

#include "wm.h"
#include "log.h"
#include "ota.h"
#include "ota_crc16.h"
#include "ota_flash.h"
#include "ota_protocol.h"
#include "string.h"
#include "sys_task.h"
#include "sw_timer_task.h"
#include "app_store_mem.h"
#include "app_global_data.h"

#define OTA_PROTO_LOG_ENABLE      1

#if OTA_PROTO_LOG_ENABLE
#define OTA_PROTO_LOG             LOG_DEBUG
#else
#define OTA_PROTO_LOG(...)
#endif

/*----------------------------macro file---------------------------*/
#define ota_reset	app_platform_reset		//Can not use app_system_reset, because it will erase ota file area.

/*----------------------------type define--------------------------*/

/*----------------------------var define---------------------------*/							
static OTA_INFO_t otaInfo;
static uint32_t tmp_recv_size;

extern OtaFlashCtl_t otaFlashCtl;

/*-------------------------func declaration------------------------*/

/*-----------------------------------------------------------------*/

static void enter_ota_mode(void)
{
	if(DEV_MODE_OTA_ING != g_app_data.sys.mode) {
		g_app_data.sys.mode = DEV_MODE_OTA_ING;
		sys_task_evt_set(SYS_EVT_OTA_ING);
	}
}


static void exit_ota_mode(void)
{
	if(DEV_MODE_OTA_ING == g_app_data.sys.mode) {
		g_app_data.sys.mode = DEV_MODE_OTA_FAIL;
		sys_task_evt_set(SYS_EVT_OTA_FAIL);
	}
}

static void ota_done(void)
{
	if(DEV_MODE_OTA_ING == g_app_data.sys.mode) {
		g_app_data.sys.mode = DEV_MODE_OTA_DONE;
		sys_task_evt_set(SYS_EVT_OTA_DONE);
	}
}

void ota_ble_disconnct_handle(void)
{
	exit_ota_mode();
}

static void otaProtocol_SendResponse(uint8_t rspOpcode, uint8_t rspValue)
{
	struct ota_ctrl_point_ntf notify;
	notify.value[0] = OTA_CODE_RESPONSE;
	notify.value[1] = rspOpcode;
	notify.value[2] = rspValue;
	notify.len = 3;
	ota_ctrl_point_notify(&notify);
}

static void otaProtocol_SendReceiptNotification(uint32_t numOfBytes)
{
	struct ota_ctrl_point_ntf notify;
	notify.value[0] = OTA_CODE_PKT_RCPT_NOTIF;
	notify.value[1] = BREAK_UINT32(numOfBytes, 0);
	notify.value[2] = BREAK_UINT32(numOfBytes, 1);
	notify.value[3] = BREAK_UINT32(numOfBytes, 2);
	notify.value[4] = BREAK_UINT32(numOfBytes, 3);
	notify.len = 5;
	ota_ctrl_point_notify(&notify);
}

static void otaProtocol_SendRecSizeResponse(uint32_t numOfBytes)
{
	struct ota_ctrl_point_ntf notify;
	notify.value[0] = OTA_CODE_RESPONSE;
	notify.value[1] = OTA_CODE_IMAGE_SIZE_REQ;
	notify.value[2] = OTA_RESP_VAL_SUCCESS;
	notify.value[3] = BREAK_UINT32(numOfBytes, 0);
	notify.value[4] = BREAK_UINT32(numOfBytes, 1);
	notify.value[5] = BREAK_UINT32(numOfBytes, 2);
	notify.value[6] = BREAK_UINT32(numOfBytes, 3);
	notify.len = 7;
	ota_ctrl_point_notify(&notify);
}


void otaProtocol_ProcessControlEvt(uint8_t *dat, uint8_t len)
{
	uint8_t opcode = dat[0];

	switch(opcode) {
		case OTA_CODE_START_OTA: {
			otaInfo.packetType = PKT_TYPE_START;
			otaInfo.imageType = dat[1];
            enter_ota_mode();
			OTA_PROTO_LOG("Start OTA,ImageType=%d\r\n", otaInfo.imageType);
		} break;

		case OTA_CODE_RECEIVE_INIT: {
			//OTA_PROTO_LOG("OTA_CODE_RECEIVE_INIT: %x, %d\n", dat[1], otaInfo.initPacketLen);
			otaInfo.packetType = PKT_TYPE_INIT;
			if(dat[1] == 0x00) {		//Receive Init Packet
				otaInfo.initPacketLen = 0;
				memset(otaInfo.initPacket, 0, sizeof(otaInfo.initPacket));
			}
			else if(dat[1] == 0x01) {	//Init Packet Complete
				if(otaInfo.initPacketLen >= 2) {
					otaInfo.imageCRC16 = BUILD_UINT16(otaInfo.initPacket[otaInfo.initPacketLen-2], otaInfo.initPacket[otaInfo.initPacketLen-1]);
					
					//PCB test mode unlock project number limitation.
					if(false == g_app_data.sys.state.pcb_test) {
						uint16_t dev_type = BUILD_UINT16(otaInfo.initPacket[otaInfo.initPacketLen-14], otaInfo.initPacket[otaInfo.initPacketLen-13]);
						uint16_t project_num = BUILD_UINT16(otaInfo.initPacket[otaInfo.initPacketLen-12], otaInfo.initPacket[otaInfo.initPacketLen-11]);
						OTA_PROTO_LOG("dev_type=%d, %d\n", dev_type, DEVICE_TYPE);
						OTA_PROTO_LOG("project_num=%d, %d\n", project_num, PROJECT_NUM);
						if(DEVICE_TYPE != dev_type || PROJECT_NUM != project_num) {
							otaProtocol_SendResponse(OTA_CODE_RECEIVE_INIT, OTA_RESP_VAL_INVALID_STATE);
						}
					}
					
					otaProtocol_SendResponse(OTA_CODE_RECEIVE_INIT, OTA_RESP_VAL_SUCCESS);
                    OTA_PROTO_LOG("Image CRC=%04x\r\n", otaInfo.imageCRC16);
				}
				else {
					otaProtocol_SendResponse(OTA_CODE_RECEIVE_INIT, OTA_RESP_VAL_OPER_FAILED);
				}
			}
			else {	//Invalid State
				otaProtocol_SendResponse(OTA_CODE_RECEIVE_INIT, OTA_RESP_VAL_INVALID_STATE);
			}
			OTA_PROTO_LOG("Receive Init\r\n");
		} break;

		case OTA_CODE_RECEIVE_FW: {
			otaInfo.packetType = PKT_TYPE_FIRMWARE_DATA;
			OTA_PROTO_LOG("Receive Fw\r\n");
		} break;

		case OTA_CODE_VALIDATE: {
			bool valid = false;
			otaInfo.packetType = PKT_TYPE_INVALID;
			
			for(uint8_t retry=0;retry<3;retry++) {
				if(true == otaFlash_CalculateCRC16(otaInfo.imageCRC16, otaInfo.appImageSize)) {
					valid = true;
					break;
				}
			}
			
			if(true == valid) {
				ota_done();
				otaProtocol_SendResponse(OTA_CODE_VALIDATE, OTA_RESP_VAL_SUCCESS);
				OTA_PROTO_LOG("Validate OK\r\n");
			}
			else {
				otaProtocol_SendResponse(OTA_CODE_VALIDATE, OTA_RESP_VAL_CRC_ERROR);
                exit_ota_mode();
				LOG_ERROR("Validate Fail\r\n");
			}
		} break;

		case OTA_CODE_ACTIVATE_N_RESET: {
			OTA_PROTO_LOG("Activate and reset\r\n");
			//otaFlash_ActivateImage(otaInfo.imageCRC16, otaInfo.appImageSize);
			otaInfo.packetType = PKT_TYPE_INVALID;
			otaInfo.imageType = OTA_IMAGE_TYPE_UNVALID;
			otaInfo.notifyPacketsNum = NUM_OF_NTF_PKT_NUM_NOT_SET;
			otaInfo.initPacketLen = 0;
			otaInfo.blImageRecSize = 0;
			otaInfo.appImageRecSize = 0;
			otaInfo.numOfNotifyRecPackets = 0;
			if(DEV_MODE_OTA_DONE != g_app_data.sys.mode) {
				ota_reset();
			}
		} break;

		case OTA_CODE_SYS_RESET: {
			OTA_PROTO_LOG("Sys reset\r\n");
			otaInfo.packetType = PKT_TYPE_INVALID;
			otaInfo.imageType = OTA_IMAGE_TYPE_UNVALID;
			otaInfo.notifyPacketsNum = NUM_OF_NTF_PKT_NUM_NOT_SET;
			otaInfo.initPacketLen = 0;
			otaInfo.blImageRecSize = 0;
			otaInfo.appImageRecSize = 0;
			otaInfo.numOfNotifyRecPackets = 0;
			if(DEV_MODE_OTA_DONE != g_app_data.sys.mode) {
				exit_ota_mode();
			}
		} break;

		case OTA_CODE_IMAGE_SIZE_REQ: {
			enter_ota_mode();
			otaInfo.packetType = PKT_TYPE_INVALID;
			if(otaInfo.imageType == OTA_IMAGE_TYPE_APPLICATION) {
				otaProtocol_SendRecSizeResponse(otaInfo.appImageRecSize);
			}
			else {
				otaProtocol_SendResponse(OTA_CODE_RECEIVE_INIT, OTA_RESP_VAL_INVALID_STATE);
			}
			OTA_PROTO_LOG("Image size Req\r\n");
		} break;

		case OTA_CODE_PKT_RCPT_NOTIF_REQ: {
			//otaInfo.packetType = PKT_TYPE_INVALID;
			tmp_recv_size = 0;
			otaInfo.notifyPacketsNum = BUILD_UINT16(dat[1], dat[2]);
			otaInfo.numOfNotifyRecPackets = 0;
			OTA_PROTO_LOG("OTA RCPT number:%d\r\n", otaInfo.notifyPacketsNum);
			if(otaInfo.notifyPacketsNum > NUM_OF_NTF_PKT_MAX_NUM) {
				otaProtocol_SendResponse(OTA_CODE_PKT_RCPT_NOTIF_REQ, OTA_RESP_VAL_DATA_SIZE);
			}
		} break;

		default:
			break;
	}
}


void otaProtocol_ProcessPacketDataEvt(uint8_t *dat, uint8_t len)
{
	switch(otaInfo.packetType) {
		case PKT_TYPE_START: {
			if(len == 12) {
				otaInfo.blImageSize = BUILD_UINT32(dat[4],dat[5],dat[6],dat[7]);
				otaInfo.appImageSize = BUILD_UINT32(dat[8],dat[9],dat[10],dat[11]);
				//otaInfo.imageType = OTA_IMAGE_TYPE_UNVALID;
				otaInfo.notifyPacketsNum = NUM_OF_NTF_PKT_NUM_NOT_SET;
				otaInfo.initPacketLen = 0; 
				otaInfo.blImageRecSize = 0;
				otaInfo.appImageRecSize = 0;
				otaFlashCtl.writeSize = 0;
				tmp_recv_size = 0;
				otaInfo.numOfNotifyRecPackets = 0;
				OTA_PROTO_LOG("BlSize=%d,AppSize=%d\r\n", otaInfo.blImageSize, otaInfo.appImageSize);
				if(otaInfo.appImageSize < APP_TEMP_BIN_MAX_SIZE) {
					otaProtocol_SendResponse(OTA_CODE_START_OTA, OTA_RESP_VAL_SUCCESS);
				}
				else {
					otaProtocol_SendResponse(OTA_CODE_START_OTA, OTA_RESP_VAL_NOT_SUPPORTED);
				}
			}
			else {	//data length is error
				otaProtocol_SendResponse(OTA_CODE_START_OTA, OTA_RESP_VAL_NOT_SUPPORTED);
			}
		} break;

		case PKT_TYPE_INIT: {
			if(otaInfo.initPacketLen + len <= sizeof(otaInfo.initPacket)) {
				memcpy(otaInfo.initPacket+otaInfo.initPacketLen, dat, len);
				otaInfo.initPacketLen += len;
			}
			else {
				otaProtocol_SendResponse(OTA_CODE_RECEIVE_INIT, OTA_RESP_VAL_NOT_SUPPORTED);
			}
		} break;

		case PKT_TYPE_FIRMWARE_DATA: {
			OTA_PROTO_LOG("-");
			otaInfo.numOfNotifyRecPackets++;
			if(otaInfo.imageType == OTA_IMAGE_TYPE_APPLICATION) {

				if(0 == tmp_recv_size && 0 == otaInfo.appImageRecSize && false == otaFlash_CheckHeader(dat, len)) {
					otaProtocol_SendResponse(OTA_CODE_RECEIVE_FW, OTA_RESP_VAL_NOT_SUPPORTED);
					return;
				}
				
				if(NUM_OF_NTF_PKT_NUM_NOT_SET == otaInfo.notifyPacketsNum) {
					otaFlash_WriteData(dat, len);
					otaInfo.appImageRecSize += len;
				}
				else {
					if(otaInfo.numOfNotifyRecPackets > NUM_OF_NTF_PKT_MAX_NUM) {
						otaProtocol_SendResponse(OTA_CODE_RECEIVE_FW, OTA_RESP_VAL_OPER_FAILED);
					}
					memcpy(&otaFlashCtl.flashBuf[(otaInfo.numOfNotifyRecPackets-1)*20], dat, len);
					tmp_recv_size += len;
					if((otaInfo.appImageRecSize + tmp_recv_size) >= otaInfo.appImageSize) {
						otaFlash_WriteData(otaFlashCtl.flashBuf, tmp_recv_size);
						otaInfo.appImageRecSize += tmp_recv_size;
						tmp_recv_size = 0;
						otaProtocol_SendResponse(OTA_CODE_RECEIVE_FW, OTA_RESP_VAL_SUCCESS);
						return;
					}
				}
				
				if(otaInfo.numOfNotifyRecPackets >= otaInfo.notifyPacketsNum) {
					otaInfo.numOfNotifyRecPackets = 0;
					otaInfo.appImageRecSize += tmp_recv_size;
					otaFlash_WriteData(otaFlashCtl.flashBuf, tmp_recv_size);
					tmp_recv_size = 0;
					otaProtocol_SendReceiptNotification(otaInfo.appImageRecSize);
					
				}
				if(otaInfo.appImageRecSize >= otaInfo.appImageSize) {
					otaProtocol_SendResponse(OTA_CODE_RECEIVE_FW, OTA_RESP_VAL_SUCCESS);
				}
			}
			else {
				otaProtocol_SendResponse(OTA_CODE_RECEIVE_FW, OTA_RESP_VAL_NOT_SUPPORTED);
			}
		} break;

		default:
			break;
	}
}


#endif // (BLE_OTA_SERVER)

